##package vocab

window.vocab = {}

( ->

##private

  LANGUAGE_TO_ENGLISH = 0
  ENGLISH_TO_LANGUAGE = 1
  SPOKEN_TO_ENGLISH = 2

  $selected_row_ids = []

  $vocab_list = null
  $vocab_carousel = null
  $data_view = null
  $grid = null
  $sort_direction = 1
  $sort_column = 'co'
  $canvas_width = undefined
  $canvas_height = undefined

  $test_type = null
  $questions = null
  $time_per_question = null
  $percent_correct = null
  $number_questions = null
  $current_question = null
  $answer_regex = null
  $timeout_timer = null
  $time_remaining = null
  $test_ended = false

  # Hack so that the grid doesn't display on top of all the vocab
  $grid_hidden = false

  $current_question_text = null
  $total_questions_text = null
  $percent_correct_text = null
  $question_text = null
  $answer_box = null
  $time_display = null

  init_properties = ->
    $current_question_text = $('#vcurq')
    $total_questions_text = $('#vqnum')
    $percent_correct_text = $('#vpcor')
    $question_text = $('#vquestion')
    $answer_box = $('#vanswerbox')
    $time_display = $('#vtdisplay')

  get_carousel_data = (vc, required_length = 20) ->
    data = []
    for i in [0..10]
      filtered_vocab = vc.filter (item) -> item.p == i
      while filtered_vocab.length > 0
        v = filtered_vocab.splice Math.floor(Math.random()*filtered_vocab.length)
        data.push "#{v.co} - #{v.tr}"
        return data if data.length == required_length
    # Still not enough in data
    until data.length == required_length
      v = vc[Math.floor(Math.random()*vc.length)]
      data.push "#{v.co} - #{v.tr}"
    return data

  push_carousel_data = (data)->
    for i in data
      wrapper = $(document.createElement('li'))
      el = $(document.createElement('a'))
      el.text(i)
      $('#vcdata ul').append(wrapper.append(el))

  show_tagcanvas = ->
    $canvas_width ?= document.getElementById('vc').offsetWidth
    $canvas_height ?= document.getElementById('vc').offsetHeight
    max_speed = if old_ie then 0 else 0.02
    init_speed = if old_ie then 0 else 0.05
    $('#vcanvas').width($canvas_width).height($canvas_height).attr("width",$canvas_width).attr("height",$canvas_height)
    TagCanvas.textFont = 'Trebuchet MS, Helvetica, sans-serif';
    TagCanvas.Start 'vcanvas', 'vcdata', {
      textFont: 'Arial, sans-serif',
      textColour: '#ffffff',
      outlineMethod: 'outline',
      outlineColour: 'rgba(255,255,255,0)',
      reverse: true,
      depth: 2,
      maxSpeed: max_speed,
      initial: [init_speed, 0],
      shape: 'vcylinder',
      lock: 'x'
    }
    ##$('#vcanvas').tagcanvas(,'vcdata')

  int_comparer = (a,b) ->
    x = a[$sort_column]
    y = b[$sort_column]
    if x == y then 0 else if x > y then 1 else -1

  #TODO: Is this too slow on IE? If so, see SlickGrid dataview example
  string_comparer = (a,b) ->
    x = a[$sort_column].toLowerCase()
    y = b[$sort_column].toLowerCase()
    x = x.replace /^\(.+\)\s*/, ""
    y = y.replace /^\(.+\)\s*/, ""
    if x == y then 0 else if x > y then 1 else -1

  grid_filter = (item, args) ->
    if args.category != "" and $.inArray args.category, item.ca
      return false
    if args.string != "" and item.co.indexOf(args.string) == -1 and item.tr.indexOf(args.string) == -1
      return false
    return true

  update_grid_filter = (category="") ->
    #TODO: Add textbox to set string filter
    $data_view.setFilterArgs { category: category, string: "" }
    $data_view.refresh()

  setup_grid = (language, v) ->
    columns = [
      {
        id:"lang", name:language, field:"co", sortable: true
      },
      {
        id:"eng", name:"English", field:"tr", sortable: true
      },
      {
        id:"prog", name:"Progress", field:"pr", width: 100, resizable: false, sortable: true,
        formatter:GraphicalPercentCompleteCellFormatter
      }
    ]
    options = {
      enableCellNavigation: true
      enableColumnReorder: false
      forceFitColumns: true
    }
    $data_view = new Slick.Data.DataView()
    $grid = new Slick.Grid "#vgrid", $data_view, columns, options
    $grid.setSelectionModel new Slick.RowSelectionModel()
    $grid.onSort.subscribe (e, args) ->
      $sort_direction = if args.sortAsc then 1 else -1
      $sort_column = args.sortCol.field
      if $sort_column == "pr"
        $data_view.sort int_comparer, args.sortAsc
      else
        $data_view.sort string_comparer, args.sortAsc
    $grid.onSelectedRowsChanged.subscribe (e) ->
      $selected_row_ids = []
      rows = $grid.getSelectedRows()
      for row in rows
        item = $data_view.getItem row
        if item then $selected_row_ids.push item.id
    $data_view.onRowCountChanged.subscribe (e,args) ->
      $grid.updateRowCount()
      $grid.render()
    $data_view.onRowsChanged.subscribe (e,args) ->
      $grid.invalidateRows args.rows
      $grid.render()
      if $selected_row_ids.length > 0
        # since the way the original data maps onto rows has changed,
        # the selected rows in the grid need to be updated
        selRows = []
        for row in $selected_row_ids
          idx = $data_view.getRowById row
          if idx != undefined
            selRows.push idx
        $grid.setSelectedRows selRows
    $data_view.beginUpdate()
    $data_view.setItems vocab.loaded_vocab
    $data_view.sort string_comparer, true
    $data_view.setFilterArgs { category: "", string: "" }
    $data_view.setFilter grid_filter
    $data_view.endUpdate()

  start_test = ->
    $test_ended = false
    nQuestions = parseInt $('#nqlist').children('.active')[0].innerHTML
    max_count = Math.ceil nQuestions / 7
    tQuestion = parseInt $('#tqlist').children('.active')[0].innerHTML.replace('s', '')
    switch $('#vflist').children('.active')[0].innerHTML
      when "Selected Words"
        words = []
        for row in $grid.getSelectedRows()
          #TODO: Could this potentially break because of using a DataView?
          words.push $grid.getDataItem row
        if words.length < 5
          $('#ent').notify 'create', { text: "You need to select 5 or more words!" }
          return
        questions = get_questions_from words, nQuestions, max_count
      when "Selected Category"
        category = $('#vleft table tr.active td')[0].innerHTML
        if category == "All Vocab"
          questions = get_questions_from vocab.loaded_vocab, nQuestions, max_count
        else
          v = vocab.loaded_vocab.filter (item) -> $.inArray(category, item.ca) > -1
          questions = get_questions_from v, nQuestions, max_count
      when "All Words"
        questions = get_questions_from vocab.loaded_vocab, nQuestions, max_count
    qType = $('#tylist').children('.active')[0].innerHTML
    if /English to/.test qType
      $test_type = ENGLISH_TO_LANGUAGE
    else if /Spoken/.test qType
      $test_type = SPOKEN_TO_ENGLISH
    else
      $test_type = LANGUAGE_TO_ENGLISH
    $questions = questions
    $percent_correct = 0
    $number_questions = nQuestions
    $current_question = 0
    $time_per_question = tQuestion
    do_test()

  get_questions_from = (vc, nQuestions, max_count) ->
    questions = []
    # Do 10-40% and 60-90%
    for i in [9..1]
      percent = i*10
      continue if percent == 50
      for word in (vc.filter (item) -> item.pr == percent).shuffle()
        count = if 10 - i > max_count then max_count else 10 - i
        n = 0
        while n < count and questions.length < nQuestions
          questions.splice Math.floor(Math.random() * questions.length), 0, word
          n++
    # Now do 50% - the default value
    for word in (vc.filter (item) -> item.pr == 50).shuffle()
      count = if 10 - i > max_count then max_count else 10 - i
      n = 0
      while n < count and questions.length < nQuestions
        questions.splice Math.floor(Math.random() * questions.length), 0, word
        n++
    while questions.length < nQuestions
      questions.push vc[Math.floor(Math.random() * vc.length)]
    i = 0
    while i < (questions.length - 1)
      if questions[i] == questions[i+1]
        x = questions[i]
        questions.splice i, 1
        rn = Math.floor(Math.random() * questions.length)
        questions.splice rn, 0, x
        i = if rn - 1 < 0 then 0 else rn - 1
      else
        i++
    questions

  do_test = ->
    console.log $test_type
    if $test_type == ENGLISH_TO_LANGUAGE
      console.log(app.input_methods[login.language].widget)
      app.input_methods[login.language].initialize()
      $('#vinputwidget').html(app.input_methods[login.language].widget)
      #TODO: Fix
      #$('#vinputwidget').jqTransform()
    $('#voverlay').fadeIn()
    $('#vtestc').fadeIn()
    $('#vbackbtn').fadeIn()
    if $('#vgrid').is ':visible'
      $('#vgrid').hide()
      $grid_hidden = true
    show_next_question()
  
  time_tick = ->
    return if $test_ended
    $time_remaining--
    if $time_remaining == 0
      next_question()
    else
      $time_display.html $time_remaining
      $timeout_timer = setTimeout time_tick, 1000

  show_next_question = ->
    q = $questions[$current_question]
    if not q
      end_test()
      show_results()
      return
    $current_question_text.html $current_question + 1
    $total_questions_text.html $number_questions
    $percent_correct_text.html $percent_correct
    $time_display.html $time_per_question
    $time_remaining = $time_per_question
    $timeout_timer = setTimeout time_tick, 1000
    #TODO: Spoken tests
    switch $test_type
      when LANGUAGE_TO_ENGLISH
        $('#vquestion').text(q.co)
        $answer_regex = get_answer_regex q.tr, LANGUAGE_TO_ENGLISH
      when ENGLISH_TO_LANGUAGE
        $('#vquestion').text(q.tr)
        $answer_regex = get_answer_regex q.co, ENGLISH_TO_LANGUAGE

  get_answer_regex = (word, mode) ->
    word = word.toLowerCase()
    word = word.replace /\s*\(([^()])\)/g, "(\\s*$1)?"
    if mode == ENGLISH_TO_LANGUAGE and languages[login.language].ignore_spaces
      word = word.replace /\s+/g, ""
    word = word.replace /\s*,\s*/g, "|"
    word = word.replace /([^ ])\/([^ ])/g, "($1|$2)"
    word = word.replace /\s*~\s*/g, '\\s*' #TODO: Put other characters in
    word = word.replace /\[.*\]/g, ""
    word = word.replace /-/g, '(\\s+|-)'
    word = word.replace /^\s*/, ''
    word = word.replace /\s*$/, ''
    word = word.replace /\s+/g, "\\s+"
    return new RegExp("^#{word}$")

  test_answer = (input, regex, mode) ->
    # Manipulate word
    input = input.toLowerCase()
    input = input.replace /,.+/g, ''
    input = input.replace /[^\w]/, ' ' unless $test_type == ENGLISH_TO_LANGUAGE
    input = input.replace /\([^()]\)/, ''
    if mode == ENGLISH_TO_LANGUAGE and languages[login.language].ignore_spaces
      input = input.replace /\s+/, ''
    else
      input = input.replace /\s+/, ' '
    input = input.replace /^\s*/, ''
    input = input.replace /\s*$/, ''
    result = regex.exec(input)
    if result and result[0] == input
      true
    else
      false

  next_question = ->
    clearTimeout($timeout_timer) if $timeout_timer
    correct = test_answer $answer_box.val(), $answer_regex, LANGUAGE_TO_ENGLISH
    $current_question++
    if correct
      $('#vcorrect').show().fadeOut('slow')
      $questions[$current_question].pr += 10
      q = $questions[$current_question]
      $questions[$current_question].pr = 120 if q.pr > 120
      $data_view.updateItem(q.id, q)
      # Don't call it in the else because it's called when #vwrong is hidden
      show_next_question()
    else
      if $test_type == ENGLISH_TO_LANGUAGE
        correct_answer = $questions[$current_question - 1].co
      else
        correct_answer = $questions[$current_question - 1].tr
      $('#vanswer').text correct_answer
      $('#vinputted').text $('#vanswerbox').val()
      $('#vwrong').show()
      $questions[$current_question].pr -= 20
      q = $questions[$current_question]
      $questions[$current_question].pr = 0 if q.pr < 0
      $data_view.updateItem(q.id, q)
    $('#vanswerbox').val('').focus()

  end_test = ->
    $test_ended = true
    to_upload = []
    $questions.map (item) -> to_upload.push { id: item.id, pr: item.pr }
    $.post '/vocabs.json', { v: $.toJSON(to_upload), u: login.user, l: login.language, c: login.class }, (data) ->
      app.reload_progress()
    $('#voverlay').fadeOut()
    $('#vwrong').fadeOut() if $('#vwrong').is ':visible'
    $('#vtestc').fadeOut()
    $('#vbackbtn').fadeOut()
    if($grid_hidden)
      $('#vgrid').show()
      $grid_hidden = false

  show_results = ->
    #TODO: Actually show results
    $('#voverlay').fadeOut()
    $('#vtestc').fadeOut()

  cancel_test = ->
    end_test()

##public

  vocab.load = (language_name, loaded_vocab) ->
    vocab.loaded_vocab = loaded_vocab
    carousel_data = get_carousel_data loaded_vocab
    push_carousel_data carousel_data
    show_tagcanvas()
    setup_grid login.language_name, loaded_vocab

  vocab.clear = ->
    $('#vc').html '''
    <canvas id="vcanvas"></canvas>
    <div id="vcdata"><ul></ul></div>
    '''
  vocab.reload_progress = (callback = ->)->
    learned = vocab.loaded_vocab.filter((v) -> v.pr >= 100).length
    total = vocab.loaded_vocab.length
    percent = Math.round((learned / total) * 100)
    $('#questionsbox').text "#{learned}/#{total} questions learned (#{percent}%)"
    callback()

##initialize

  vocab.initialize = ->
    init_properties()
    content_width = $('#dummyc')[0].offsetWidth
    $('#vgrid').css("width", content_width + "px")
    $('#vleft table tr').click ->
      $('#vleft table tr').removeClass 'active'
      $(this).addClass 'active'
      if $(this).find('td')[0].innerHTML.indexOf("Cloud") > -1
        $('#vl').hide()
        $('#vc').show()
      else
        $('#vc').hide()
        $('#vl').show()
        filter = $(this).find('td')[0].innerHTML
        #TODO: Filter DataView
        if filter.indexOf("All Vocab") > -1
          update_grid_filter()
        else
          update_grid_filter filter
        $grid.autosizeColumns()
    $('#nqlist .rb').click ->
      $('#nqlist .rb').removeClass('active')
      $(this).addClass('active')
    $('#tqlist .rb').click ->
      $('#tqlist .rb').removeClass('active')
      $(this).addClass('active')
    $('#vflist .rb').click ->
      $('#vflist .rb').removeClass('active')
      $(this).addClass('active')
    $('#tylist .rb').click ->
      $('#tylist .rb').removeClass('active')
      $(this).addClass('active')
    $('#vstbtn').click ->
      start_test()
    $('#vanswerbox').keyup (e) ->
      if $test_type == ENGLISH_TO_LANGUAGE
        $('#vanswerbox').val app.input_methods[login.language].key_release($('#vanswerbox').val())
      if e.keyCode == 13
        $('#vsubmitbtn').click()
    $('#vsubmitbtn').click ->
      next_question()
    $('#vbackbtn').click ->
      cancel_test()
    $('#vsubmit').click ->
      $('#vwrong').hide()
      show_next_question()

)()